﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using Celeriq.Common;
using Celeriq.Server.Interfaces;
using Celeriq.Utilities;
using System.IO;

namespace Celeriq.Server.Core
{
    [Serializable]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class RepositoryManager : Celeriq.Common.IDataModel//, Celeriq.Server.Interfaces.IRepositoryService
    {
        private Celeriq.Client.Interfaces.ISystemCore _system = null;
        private Dictionary<Guid, RemotingObjectCache> _repositoryList = new Dictionary<Guid, RemotingObjectCache>();

        public RepositoryManager(Celeriq.Client.Interfaces.ISystemCore system)
        {
            _system = system;
        }

        //public void AddRepository(Guid repositoryId, RemotingObjectCache repository)
        //{
        //    _repositoryList.Add(repositoryId, repository);
        //}

        public void RemoveRepository(Guid repositoryId)
        {
            _repositoryList.Remove(repositoryId);
        }

        public bool Contains(Guid repositoryId)
        {
            return _repositoryList.ContainsKey(repositoryId);
        }

        public IEnumerable<RemotingObjectCache> List
        {
            get { return _repositoryList.Values; }
        }

        public Celeriq.Server.Interfaces.IRepository AddRepository(Guid repositoryId)
        {
            try
            {
                if (_repositoryList.ContainsKey(repositoryId))
                    return _repositoryList[repositoryId].ServiceInstance;

                //Try to load the repository
                var fileName = Path.Combine(ConfigurationSettings.DataPath, repositoryId.ToString() + ".celeriq");
                if (File.Exists(fileName))
                {
                    var repositoryDef = new RepositorySchema();
                    repositoryDef.Load(fileName);
                    repositoryDef.CachePath = Path.Combine(ConfigurationSettings.DataPath, repositoryDef.ID.ToString());

                    var startUp = new Celeriq.Server.Interfaces.ServiceStartup()
                    {
                        RepositoryFolder = ConfigurationSettings.DataPath,
                        RepositorySchema = repositoryDef,
                        CacheLength = 300,
                    };

                    var repository = new Celeriq.RepositoryAPI.Repository(startUp, ConfigurationSettings.MasterKeys, _system);
                    var remoteItem = new RemotingObjectCache() { ServiceInstance = repository, Repository = repositoryDef };
                    remoteItem.VersionHash = repositoryDef.VersionHash;
                    _repositoryList.Add(repositoryId, remoteItem);
                }

                if (_repositoryList.ContainsKey(repositoryId))
                    return _repositoryList[repositoryId].ServiceInstance;
                return null;
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public DateTime GetLastAccess(Guid repositoryId)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                return repository.ServiceInstance.LastAccess;

            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public void UnloadData(Guid repositoryId)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                repository.ServiceInstance.UnloadData();
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }

        public void LoadData(Guid repositoryId, UserCredentials credentials)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                repository.ServiceInstance.Query(new DataQuery() { Credentials = credentials });
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }

        public string[] UpdateData(Guid repositoryId, IEnumerable<DataItem> list, UserCredentials credentials)
        {
            var errorList = new List<string>();
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null)
                    errorList.Add("The repository has not been initialized!");
                else
                    repository.ServiceInstance.UpdateIndexList(list, credentials);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                errorList.Add(ex.ToString());
            }
            return errorList.ToArray();
        }

        public string[] DeleteData(Guid repositoryId, IEnumerable<DataItem> item, UserCredentials credentials)
        {
            var errorList = new List<string>();
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null)
                    errorList.Add("The repository has not been initialized!");
                else
                    repository.ServiceInstance.DeleteData(item, credentials);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                errorList.Add(ex.ToString());
            }
            return errorList.ToArray();
        }

        public string[] Clear(Guid repositoryId, UserCredentials credentials)
        {
            var errorList = new List<string>();
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null)
                {
                    errorList.Add("The repository has not been initialized!");
                }
                else
                {
                    repository.ServiceInstance.Clear(credentials);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                errorList.Add(ex.ToString());
            }
            return errorList.ToArray();
        }

        public DataQueryResults Query(Guid repositoryId, DataQuery query)
        {
            var retval = new DataQueryResults();
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null)
                {
                    retval.ErrorList = new string[] { "The repository has not been initialized!" };
                }
                else
                {
                    retval = repository.ServiceInstance.Query(query);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                retval.ErrorList = new string[] { ex.ToString() };
            }
            return retval;
        }

        public void ShutDown(Guid repositoryId)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null)
                    throw new Exception("The repository has not been initialized!");

                repository.ServiceInstance.ShutDown();
                repository = null;

            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        /// <summary>
        /// Clears all data from the repository
        /// </summary>
        /// <param name="startup"></param>
        public void Reset(Guid repositoryId, Celeriq.Server.Interfaces.ServiceStartup startup)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                repository.ServiceInstance.Reset(startup);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        /// <summary>
        /// Copies the entire repository to an archive file
        /// </summary>
        public bool ExportSchema(Guid repositoryId, Celeriq.Server.Interfaces.ServiceStartup startup, UserCredentials credentials, string backupFile)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                return repository.ServiceInstance.ExportSchema(startup, credentials, backupFile);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        /// <summary>
        /// Copies the entire repository to an archive file
        /// </summary>
        public bool Backup(Guid repositoryId, Celeriq.Server.Interfaces.ServiceStartup startup, UserCredentials credentials, string backupFile)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);                
                return repository.ServiceInstance.Backup(startup, credentials, backupFile);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool Restore(Guid repositoryId, Celeriq.Server.Interfaces.ServiceStartup startup, UserCredentials credentials, string backupFile)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                return repository.ServiceInstance.Restore(startup, credentials, backupFile);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        /// <summary>
        /// Returns the size on disk of the repository data
        /// </summary>
        /// <returns></returns>
        public long GetDataDiskSize(Guid repositoryId, UserCredentials credentials)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null) return 0;
                return repository.ServiceInstance.GetDataDiskSize(credentials);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        /// <summary>
        /// Returns the size of memory of the repository data
        /// </summary>
        /// <returns></returns>
        public long GetDataMemorySize(Guid repositoryId, UserCredentials credentials)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null) return 0;
                return repository.ServiceInstance.GetDataMemorySize(credentials);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        /// <summary>
        /// Returns the number of items in the repository
        /// </summary>
        /// <returns></returns>
        public long GetItemCount(Guid repositoryId, UserCredentials credentials)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null) return 0;
                return repository.ServiceInstance.GetItemCount(credentials);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public void Compress(Guid repositoryId, UserCredentials credentials)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null)
                    throw new Exception("The repository has not been initialized!");

                repository.ServiceInstance.Compress(credentials);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public bool IsLoaded(Guid repositoryId)
        {
            try
            {
                AddRepository(repositoryId);
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                if (repository == null)
                    return false;
                else
                    return repository.IsLoaded;
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

        public ProfileItem[] GetProfile(Guid repositoryId, UserCredentials credentials, long lastProfileId)
        {
            try
            {
                var repository = this.List.FirstOrDefault(x => x.Repository.ID == repositoryId);
                return repository.ServiceInstance.GetProfile(credentials, lastProfileId);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }

    }
}